#include "Task.hpp"
#include<bits/stdc++.h>
#include<sys/types.h>
#include<unistd.h>
#include<sys/wait.h>
#include<stdlib.h>
#include<sys/stat.h>
#include<string.h>
#include<fcntl.h>
#include<ctime>
using namespace std;
vector<Task_t> taskv;

class channel{
  public:
  channel(int fd,int pid,string name)
    :_fd(fd)
    ,_pid(pid)
    ,_name(name)
    {}
  public:
  
    int _fd; //发送任务的文件描述符
    pid_t _pid;//子进程的PID
    string _name; //子进程的名字

};
void Menu()
{
  cout <<"#########菜单##########" << endl;
  cout << "1:task1" << endl;
  cout << "2:task2" << endl;
  cout << "3:task3" << endl;
  cout << "4:task4" << endl;
  cout << "5:退出" << endl;
  cout << "##################" << endl;
}
void slaver()
{
   //read(0);->管道
    while(1){
        
       
        //这里有点小问题
        //父进程拿到3 4 关闭3 拿到4
        //下次3 5 关闭3 拿到5 
        //所以这里打印出来的都是3
        //而父进程的pipefd[1]没问题,因为他是写端
        //怎么解决？？ ？？ 
        //cout << rfd << endl;

        int cmdcode = 0;
        int n = read(0,&cmdcode,sizeof(cmdcode));
        //父进程不给子进程发数据就会阻塞等待

        if(n == sizeof(int)) {
          //执行cmdcode对应的任务列表
          //cout << getpid() << " cmdcode:" << cmdcode << endl;
          cout << "子进程执行任务：" << endl;
          taskv[cmdcode]();
        }
        if(n == 0) break;
    }
}

//格式化
//输入:const & 
//输出 *
//输入输出 & 
const int processnum = 10;
vector<channel> channels;
void InitProcessPool()
{
  vector<int> oldfds;
  for(int i = 0;i < processnum;++i)
  {
     int pipefd[2]; //临时空间
     int n = pipe(pipefd); // 3 4
     assert(!n);
  

     pid_t id = fork();
     if(id == 0)
     {
       //child
      for(auto fd:oldfds) close(fd);
       close(pipefd[1]);
       dup2(pipefd[0],0);
       //slaver(pipefd[0]);
       
       slaver();
       exit(0);
     }
     close(pipefd[0]);

     //添加channel字段
     string name = "process->" + to_string(i);
     channels.push_back(channel(pipefd[1],id,name));
  
   oldfds.push_back(pipefd[1]);      
  }
}
void Debug()
{
   //test
   for(const auto& e:channels)
   {
     cout << e._fd << " " << e._pid << " " << e._name << endl;
   }
}
void ControlProcess()
{
   while(1)
   {
    Menu();
    int x;cin >> x;
    //1.选择任务
    //int cmdcode = rand() % 20;
    while(x <= 0 || x > 5 )
    {
      cout << "input Error! please input again:" << endl;
      cin >> x;
    }
    if(x == 5) break;
    int cmdcode = x - 1;
    //2.选择进程
    int pronum = rand() % processnum;

    cout << "父进程发送任务给processpid: " << channels[pronum]._pid << " processname: " << channels[pronum]._name << endl;
    //3.发送任务
    write(channels[pronum]._fd,&cmdcode,sizeof(cmdcode));
    sleep(1);
   }
}
void QuitProcess()
{
  cout << "Quit 开始" << endl;
//   for(const auto&c:channels)close(c._fd);
//  // sleep(10);
//   for(const auto&c:channels)waitpid(c._pid,nullptr,0);


   //这么写会有问题
   //fork出子进程后,子进程会复制父进程的文件描述符表
   //也就是说第二次创建的子进程也继承了父进程对第一个子进程创建管道的写端！
   //同理,都会继承下去,如果先close() 再waitpid,此时对应的写端没有关闭完全,还是阻塞状态
   //就会卡住无法wait！
    for(const auto&c:channels)
  {
    close(c._fd);
   waitpid(c._pid,nullptr,0);
  }


   //解决方法
   //1.倒着回收
  //  int last = channels.size() - 1;
  //  for(int i = last;i >= 0;i--){
  //   close(channels[i]._fd );
  //   waitpid(channels[i]._pid,nullptr,0);
  //  }
 
 //2.解决本质问题,确保每一个子进程都只有一个写端


   //如果全close,就可以
 

}
int main()
{
  //function<void()> f[] = {task1,task2,task3,task4};
  DownLoad(&taskv);
  srand(time(nullptr) ^ getpid() ^ 1023);
  //1.初始化
  InitProcessPool();
   //test
  // Debug();
  //2.开始控制子进程
 ControlProcess();
  //3.清理收尾
 //
 QuitProcess();

 //sleep(3);
  return 0;
}
